package fr.insta.reverse.uml.controller;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.bcel.classfile.JavaClass;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import fr.insta.reverse.uml.bean.Projet;
import fr.insta.reverse.uml.bean.User;
import fr.insta.reverse.uml.bean.Visibilite;
import fr.insta.reverse.uml.java.projectParser.ServiceFile;
import fr.insta.reverse.uml.java.projectParser.ServiceUML;
import fr.insta.reverse.uml.routing.Route;
import fr.insta.reverse.uml.service.IService;
import fr.insta.reverse.uml.service.ServiceFactory;
import fr.insta.reverse.uml.service.ServiceName;
import fr.insta.reverse.uml.service.ServiceProject;
import fr.insta.reverse.uml.service.ServiceUser;
import fr.insta.reverse.uml.service.ServiceVisibilite;
import fr.insta.reverse.uml.util.IhmValues;
import fr.insta.reverse.uml.util.InstaUtils;

public class ProjectController extends DefaultController
{

	public ProjectController(HttpServletRequest _request, HttpServletResponse _response)
	{
		super(_request, _response);
	}

	@Override
	public void execute()
	{
		String route = (String) request.getAttribute("route");

		if (route.contentEquals(Route.ProjectAccessRule.getRoute()))
		{
			projectAccessRule();
		}
		else if (route.contentEquals(Route.ProjectCreate.getRoute()))
		{
			projectCreate();
		}
		else if (route.contentEquals(Route.ProjectUmlGeneration.getRoute()))
		{
			projectUmlGeneration();
		}
		else if (route.contentEquals(Route.ProjectDownload.getRoute()))
		{
			projectDownload();
		}
		else if (route.contentEquals(Route.ProjectSearch.getRoute()))
		{
			projectSearch();
		}
		else if (route.contentEquals(Route.ProjectView.getRoute()))
		{
			projectView();
		}
		else if (route.contentEquals(Route.ProjectList.getRoute()))
		{
			projectList();
		}
	}

	private void projectUmlGeneration()
	{
		List<String> diagramList = (List<String>) request.getSession().getAttribute("scriptUml");

		request.getSession().setAttribute("scriptUml", null);

		//Means we are not coming from the project creation
		if (diagramList == null)
		{
			redirect(Route.Accueil);
			return;
		}

		StringBuilder umlBuilder = new StringBuilder();

		for (String umlLine : diagramList)
		{
			umlBuilder.append(umlLine);
			umlBuilder.append("\n");
		}

		String yumlScript = umlBuilder.toString();

		yumlScript = yumlScript.replace("<", "&lt;");
		yumlScript = yumlScript.replace(">", "&gt;");

		System.out.println("YUML SCRIPT WITH ALTER : " + yumlScript);

		request.setAttribute("diagram", yumlScript);
		request.setAttribute("useJquery", true);

		Object hash = request.getSession().getAttribute("hash");

		//Means a project has been created before, and we need to pass the hash value
		//To insert the right image
		if (hash != null)
		{
			request.setAttribute("hash", hash);
			request.getSession().setAttribute("hash", null);
		}

		dispatch("projectUmlCreation");
	}

	private void projectView()
	{
		ServiceProject sp = (ServiceProject) ServiceFactory.getService(ServiceName.ServiceProject);
		int pid = Integer.parseInt(request.getParameter("idProject"));
		Projet p = sp.get(pid);
		request.setAttribute("projet", p);
		dispatch("projectView");
	}

	private void projectSearch()
	{
		if (request.getMethod() == "POST")
		{
			ServiceProject sp = (ServiceProject) ServiceFactory.getService(ServiceName.ServiceProject);
			String plib = request.getParameter("projectLib");
			User usr = (User) request.getSession().getAttribute("User");
			List<Projet> list = sp.listProjectsByLib(new Projet(plib, usr));
			request.setAttribute("list_projets", list);
			dispatch("projectList");
		}
		else
		{
			dispatch("projectSearch");
		}
	}

	private void projectDownload()
	{
		dispatch("projectDownload");
	}

	private void projectCreate()
	{
		IService<Visibilite> serviceVisibilite = (ServiceVisibilite) ServiceFactory.getService(ServiceName.ServiceVisibilite);

		Projet project = null;
		User user = (User) request.getSession().getAttribute("User");

		if (request.getMethod() == "POST")
		{
			ServiceProject serviceProjet = (ServiceProject) ServiceFactory.getService(ServiceName.ServiceProject);

			try
			{
				ServletFileUpload upload = initPostUpload();

				List<FileItem> formParameters = upload.parseRequest(request);
				HashMap<String, Object> formFields = getFormFields(formParameters);

				boolean publish = (boolean) formFields.get(IhmValues.FORM_UML_CREATE_PUBLISH);
				String projectErrorMessage = null;

				if (publish && request.getSession().getAttribute("User") != null)
				{
					project = new Projet();

					project.setProp(user);
					project.setLib((String) formFields.get(IhmValues.FORM_UML_CREATE_NAME));

					int idVisibilite = (int) formFields.get(IhmValues.FORM_UML_CREATE_VISIBILITY);

					project.setVisibilite(serviceVisibilite.get(idVisibilite));

					//Create a unique value to check when the image will be inserted from ajax
					String hash = InstaUtils.sha1(user.getMail() + System.currentTimeMillis());

					project.setImgHash(hash);

					projectErrorMessage = serviceProjet.getMessageErrorForNewProject(project);
				}

				if (publish && projectErrorMessage != null)
				{
					setMessage(projectErrorMessage);
				}
				else
				{
					List<String> scriptUML = getUmlScriptFromFile((FileItem) formFields.get(IhmValues.FORM_UML_CREATE_FILE));

					//scriptUML.add(0, "/*This came from a student scholar project to convert java class files to uml diagram. ");
					//scriptUML.add(1, "Thanks in advance to let us use this awesome uml generator for educational purpose !*/");

					request.getSession().setAttribute("scriptUml", scriptUML);

					if (publish && project != null)
					{
						request.getSession().setAttribute("hash", project.getImgHash());
						project = serviceProjet.insert(project);
					}

					redirect(Route.ProjectUmlGeneration);
					return;
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		List<Visibilite> objects = serviceVisibilite.getObjects();

		request.setAttribute("isConnected", request.getSession().getAttribute("User") != null);
		request.setAttribute("listVisibilites", objects);

		dispatch("projectCreate");
	}

	private ArrayList<String> getUmlScriptFromFile(FileItem _fileItem)
	{
		ServiceFile serviceFile = new ServiceFile();

		File tempFile = serviceFile.getTempFile(request.getServletContext(), _fileItem);
		ZipFile zipFile = serviceFile.getZipFile(tempFile);

		List<JavaClass> javaClasses = serviceFile.getJavaClasses(zipFile);

		ServiceUML sUML = new ServiceUML(javaClasses);

		return sUML.generate();
	}

	private HashMap<String, Object> getFormFields(List<FileItem> _formParameters)
	{
		HashMap<String, Object> formFields = new HashMap<>();

		boolean publish = false;
		String name = null;
		int visibility = 0;
		FileItem file = null;

		for (FileItem item : _formParameters)
		{
			if (item.getFieldName().contentEquals(IhmValues.FORM_UML_CREATE_PUBLISH))
			{
				publish = item.getString() != null;
			}
			else if (item.getFieldName().contentEquals(IhmValues.FORM_UML_CREATE_NAME))
			{
				name = item.getString();
			}
			else if (item.getFieldName().contentEquals(IhmValues.FORM_UML_CREATE_VISIBILITY))
			{
				try
				{
					visibility = Integer.parseInt(item.getString());
				}
				catch (Exception _e)
				{
					visibility = 0;
				}
			}
			else if (item.getFieldName().contentEquals(IhmValues.FORM_UML_CREATE_FILE))
			{
				file = item;
			}
		}

		formFields.put(IhmValues.FORM_UML_CREATE_PUBLISH, publish);
		formFields.put(IhmValues.FORM_UML_CREATE_NAME, name);
		formFields.put(IhmValues.FORM_UML_CREATE_VISIBILITY, visibility);
		formFields.put(IhmValues.FORM_UML_CREATE_FILE, file);

		return formFields;
	}

	private ServletFileUpload initPostUpload()
	{
		final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB
		final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB
		final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB

		File tempDir = new File(System.getProperty("java.io.tmpdir"));

		DiskFileItemFactory factory = new DiskFileItemFactory();

		factory.setSizeThreshold(MEMORY_THRESHOLD);
		factory.setRepository(tempDir);

		ServletFileUpload upload = new ServletFileUpload(factory);

		upload.setFileSizeMax(MAX_FILE_SIZE);
		upload.setSizeMax(MAX_REQUEST_SIZE);

		return upload;
	}

	private void projectAccessRule()
	{
		dispatch("projectAccessRule");
	}

	private void projectList()
	{
		ServiceProject serviceProjet = (ServiceProject) ServiceFactory.getService(ServiceName.ServiceProject);
		ServiceUser serviceUser = (ServiceUser) ServiceFactory.getService(ServiceName.ServiceUser);
		List<Projet> listProjets = new ArrayList<Projet>();

		// Si l'utilisateur n'est pas connecté on n'affiche que les projets publiques
		// sinon on affiche d'abord ses projets puis les projets publiques
		User user = serviceUser.getSessionUser(request);
		if (user != null)
		{
			listProjets = serviceProjet.listUserAndPublicProjects(user);
		}
		else
		{
			listProjets = serviceProjet.listPublicProjects();
		}

		request.setAttribute("list_projets", listProjets);
		dispatch("projectList");
	}
}
